package com.woniu.zsd.user.config;


import com.woniu.commons.util.JwtUtil;
import com.woniu.commons.util.RedisKey;
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.ExpiredJwtException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.concurrent.TimeUnit;


@Component
public class UserLoginInterceptor implements HandlerInterceptor {
    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private JwtUtil jwtUtil;
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        // 排除options请求
        if (request.getMethod().equals("OPTIONS")) {
            return true;
        }
        System.out.println(request.getRequestURI());
        // 取出请求头中的token
        String accessToken = request.getHeader("token");
        if (accessToken == null) {
            throw new Exception("未登录");
        }
        if (stringRedisTemplate.hasKey(RedisKey.ACCESS_TIME_OUT(accessToken))) {
            // 说明accessToken未过期
            // 校验未过期的短token是否和服务器记录的最后一个有效token一致
            String userId = stringRedisTemplate.opsForValue().get(RedisKey.ACCESS_TIME_OUT(accessToken));
            String serverAccessToken = stringRedisTemplate.opsForValue().get(RedisKey.USER_ACCESS_TOKEN_KEY(userId));
            if (!accessToken.equals(serverAccessToken)) {
                throw new Exception("账户在其他地方登录");
            }
        } else {
                // 通过redis判断当前的短token是否过期
                synchronized (accessToken.intern()) {
                    if (!stringRedisTemplate.hasKey(RedisKey.ACCESS_TIME_OUT(accessToken))) {
                        //当前的accessToken已过期
                        // 取出refreshToken
                        String refreshToken = stringRedisTemplate.opsForValue().get(RedisKey.USER_REFRESH_TOKEN_KEY(accessToken));
                        if (refreshToken == null) {
                            throw new ExpiredJwtException(null, null, null);
                        }
                        Claims body = jwtUtil.parseToken(refreshToken);
                        String userId = body.get("id").toString();

                        // 续期
                        // 防止并发时多个请求同时进行续期给RedisKey.ACCESS_TIME_OUT添加5秒过期时间
                        String newAccessToken = null;
                        stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_TIME_OUT(accessToken),"", 5, TimeUnit.SECONDS);

                        newAccessToken = jwtUtil.createToken(body, 1);
                        String newRefreshToken = jwtUtil.createToken(body, 60 * 24);

                        // 将refreshToken保存到redis中
                        stringRedisTemplate.opsForValue().set(RedisKey.USER_REFRESH_TOKEN_KEY(newAccessToken), newRefreshToken, 60 * 24, TimeUnit.MINUTES);

                        // 使用redis记录当前用户所使用的最后一个短token
                        stringRedisTemplate.opsForValue().set(RedisKey.USER_ACCESS_TOKEN_KEY(userId), newAccessToken, 1, TimeUnit.MINUTES);

                        // 在redis中保存新的accessToken的过期时间
                        stringRedisTemplate.opsForValue().set(RedisKey.ACCESS_TIME_OUT(newAccessToken), userId, 1, TimeUnit.MINUTES);

                        // 响应accessToken到前端
                        response.setHeader("token", newAccessToken);
                    }
                }
            }
            return true;
        }
}